package com.auth.config;

import com.auth.config.service.BaseClientDetailService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.ClientDetailsService;
import org.springframework.security.oauth2.provider.error.WebResponseExceptionTranslator;
import org.springframework.security.oauth2.provider.token.DefaultTokenServices;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.InMemoryTokenStore;

import javax.annotation.Resource;
import javax.sql.DataSource;
import java.util.concurrent.TimeUnit;

/**
 * auth 授权服务器配置,授权认证中心
 */
@Configuration
@EnableAuthorizationServer
@Order(Integer.MIN_VALUE)
public class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {

    private static final Logger log = LoggerFactory.getLogger(AuthorizationServerConfiguration.class);

    // 注入认证管理
    @Autowired
    AuthenticationManager authenticationManager;

    // 方案 一：采用redis缓存服务存储token
    @Autowired
    RedisConnectionFactory redisConnectionFactory;

    // 方案二 使用内存存储token
    @Autowired
    private TokenStore tokenStore;

    @Autowired
    private DataSource dataSource;
    /*** 自定义登录或者鉴权失败时的返回信息
     */
    @Resource(name = "webResponseExceptionTranslator")
    private WebResponseExceptionTranslator webResponseExceptionTranslator;


    /**
     * 用来配置授权（authorization）以及令牌（token）的访问端点和令牌服务(token services)。
     * 核心配置  在启动时就会进行配置
     *
     * @param endpoints
     * @throws Exception
     */
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        log.info("======================^_^ start 生成token ^_^========================");

        endpoints
                .tokenStore(new MyRedisTokenStore(redisConnectionFactory))
                .authenticationManager(authenticationManager)//客户端验证
                .allowedTokenEndpointRequestMethods(HttpMethod.GET, HttpMethod.POST);
//                .userDetailsService(userService); //配置userService 这样每次认证的时候会去检验用户是否锁定，有效等

        DefaultTokenServices tokenService = new DefaultTokenServices();
        tokenService.setTokenStore(endpoints.getTokenStore());
        tokenService.setSupportRefreshToken(true);
        tokenService.setClientDetailsService(endpoints.getClientDetailsService());
        tokenService.setTokenEnhancer(endpoints.getTokenEnhancer());
        tokenService.setAccessTokenValiditySeconds((int) TimeUnit.HOURS.toSeconds(12)); // 12 小时
        tokenService.setRefreshTokenValiditySeconds((int) TimeUnit.DAYS.toSeconds(7));  // 7天
        tokenService.setReuseRefreshToken(false);
        endpoints.tokenServices(tokenService);//指定spring要保护的资源，如果没有这个，访问控制的时候会说没有Authentication object：
        //自定义登录或者鉴权失败时的返回信息
        endpoints.exceptionTranslator(webResponseExceptionTranslator);
    }

    /**
     * 用来配置令牌端点(Token Endpoint)的安全约束.
     *
     * @param oauthServer
     * @throws Exception
     */
    @Override
    public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
        log.info("======================^_^ start 校验 token ^_^========================");
        //允许表单认证
        //这里增加拦截器到安全认证链中，实现自定义认证，包括图片验证，短信验证，微信小程序，第三方系统，CAS单点登录
        //addTokenEndpointAuthenticationFilter(IntegrationAuthenticationFilter())
        //IntegrationAuthenticationFilter 采用 @Component 注入
        oauthServer.allowFormAuthenticationForClients()
                .tokenKeyAccess("isAuthenticated()")
                .checkTokenAccess("permitAll()");
    }

    //基于方案二：使用内存的tokenStore
    @Bean
    public TokenStore tokenStore() {
        return new InMemoryTokenStore();
    }

    /**
     * 我们必须分配 client_id, client_secret才行。
     * Spring Security OAuth2的配置方法是编写@Configuration类继承AuthorizationServerConfigurerAdapter，
     * 然后重写void configure(ClientDetailsServiceConfigurer clients)方法
     * 客户端详情信息在这里进行初始化，  数据库在进行client_id 与 client_secret验证时   会使用这个service进行验证
     *
     * @param clients
     * @throws Exception
     */
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.withClientDetails(clientDetails());
        // clients.jdbc(dataSource);
    }

    @Bean
    public ClientDetailsService clientDetails() {
        return new BaseClientDetailService();
    }

}